Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed handling of long custom assertions #45

Merged
merged 1 commit into from
Jun 26, 2018
Merged

Fixed handling of long custom assertions #45

merged 1 commit into from
Jun 26, 2018

Conversation

jameslamb
Copy link
Contributor

This PR addresses #43 . You can use the code reported there to reproduce.

Given a super long assertion like this:

assert_that(4 %>% sqrt %>% multiply_by(10) %>% divide_by(5) %>% add(13) %>% subtract(20) %>% equals(0))

see_if returns multiple lines of the call and results in this error:

Error in stop(assertError(attr(res, "msg"))) : bad error message

As of the changes on this PR, the behavior will be closer to what is expected:

> assert_that(4 %>% sqrt %>% multiply_by(10) %>% divide_by(5) %>% add(13) %>% subtract(20) %>% equals(0))
Error: `%>%`(lhs = 4 %>% sqrt %>% multiply_by(10) %>% divide_by(5) %>% ... is not TRUE

Thank you for considering this PR.

Copy link
Owner

@hadley hadley left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please add a unit test too?

R/assert-that.r Outdated
fail_default <- function(call, env) {
call_string <- deparse(call, width.cutoff = 60L)
if (length(call_string) > 1L) ch <- paste0(call_string[1L], "...")
if (length(call_string) > 1L){
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs a space after )

@jameslamb
Copy link
Contributor Author

I made the recommended changes. Let me know if you think anything else needs to be done!

@codecov-io
Copy link

codecov-io commented Jun 26, 2018

Codecov Report

Merging #45 into master will increase coverage by 3.7%.
The diff coverage is 100%.

Impacted file tree graph

@@            Coverage Diff            @@
##           master      #45     +/-   ##
=========================================
+ Coverage   73.56%   77.27%   +3.7%     
=========================================
  Files           9        9             
  Lines          87       88      +1     
=========================================
+ Hits           64       68      +4     
+ Misses         23       20      -3
Impacted Files Coverage Δ
R/assert-that.r 86.48% <100%> (+8.7%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update f72009c...21f798b. Read the comment docs.

test_that("assert_that handles long false assertions gracefully", {
expect_error(
assert_that(isTRUE(10 + sqrt(25) + sum(1:10) + sqrt(25) + sum(11:20) + sqrt(25) + sum(21:30) + sqrt(25) + sum(31:40) + sqrt(25) + sum(41:50))),
"^isTRUE\\(x = 10 \\+ sqrt\\(25\\) \\+ sum\\(1\\:10\\) \\+ sqrt\\(25\\) \\+ sum\\(11\\:20\\) \\+ \\.\\.\\. is not TRUE$"
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The point of the regular expression is to make sure that you've got the right error message, not to exactly match the text. To that end, I'd suggest just matching [.]{3}.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I disagree, actually. This test as it's written would catch a change that accidentally suppresses the first part of the error message and returns a NULL (which can happen silently in lots of settings).

A test with [.]{3} would pass if that happened and the error message was just ... is not TRUE

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then make it a little more specific; your current test is too fragile.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok sure. Changed to "^isTRUE\\(.* [.]{3} is not TRUE$" to catch the idea that at least some of the code passed in the expression is returned in the result

)
})

test_that("assert_that handles long true assertions gracefully", {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think you need this test. Doesn't it test the same code as the previous test?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. In the first test, the condition evaluates to FALSE and throws an error.

In this test, I changed the condition slightly so it evaluates to TRUE and passes. I think it's a generally useful habit to get in to test behavior on both side of a fork in the code caused by some condition being TRUE / FALSE.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh in that case I think it's even less useful. Why do we expect long successful conditions to behave differently to short successful conditions?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would catch an issue if, for example, someone submitted a PR that tries to format the potential error message before evaluating the condition (causing deparse() to return more than one element).

I don't feel THAT strongly about it. If you tell me to drop it, I'll drop it.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then that would be caught by review. I don't think the point of tests is to protect against "attacks" by contributors.

Copy link
Contributor Author

@jameslamb jameslamb Jun 26, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed the test. This is your package and your call.

But I do respectfully disagree. I'm not talking about malicious attacks by contributors. Well-meaning contributions can still make subtle breaking changes.

In my opinion, CI is a way to offload to a computer the task of checking all the things that you want to remain true about your code. Keeping all that knowledge in your head opens you up to human error (have you ever reviewed PRs before your first cup of coffee??) and makes it difficult to share review responsibility across multiple reviewers.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my experience, fragile tests of precise error messages have cause many more problems than they solve.

Adding tests for things that might potentially go wrong is of little value; spend time writing tests for high risk areas that are not already covered.

@hadley hadley merged commit ec90526 into hadley:master Jun 26, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants